﻿using System;
using System.Collections;
using System.IO;
using System.Linq;
using Microsoft.WowAddonStudio.FrameXml.Serialization.Support;
using NUnit.Framework;

namespace Microsoft.WowAddonStudio.FrameXml.Serialization.Tests
{
	public abstract class SerializationTestBase<TSerializationObject> where TSerializationObject : SerializationObject
	{
		public const string WowNamespace = "http://www.blizzard.com/wow/ui/";
		protected const string frameXmlFolder = "Data";
		private const string schemaPath = "Data/UI.xsd";

		/// <summary>
		/// Gets or sets the test entity.
		/// </summary>
		/// <value>The test entity.</value>
		protected TSerializationObject TestEntity { get; set; }

		/// <summary>
		/// Gets or sets the frame XML loader.
		/// </summary>
		/// <value>The frame XML loader.</value>
		protected FrameXmlLoader FrameXmlLoader { get; private set; }

		[SetUp]
		public void SetUp()
		{
			FrameXmlLoader = new FrameXmlLoader {SchemaPath = schemaPath};
		}

		[TearDown]
		public void TearDown()
		{
			if (TestEntity != null && Assert.Counter == 0)
				AssertNoUnknownElements(TestEntity);
		}

		/// <summary>
		/// Initializes the test entity from a FrameXML file.
		/// </summary>
		/// <param name="frameXmlPath">The frameXML path.</param>
		protected void InitializeTestEntity(string frameXmlPath)
		{
			TestEntity = GetTestEntity(FrameXmlLoader.LoadUi(Path.Combine(frameXmlFolder, frameXmlPath)));
		}

		/// <summary>
		/// Gets the entity to be tested from a <see cref="Ui"/> instance.
		/// </summary>
		/// <param name="ui">The ui instance.</param>
		/// <returns>An instance of the <typeparamref name="TSerializationObject"/> class.</returns>
		protected virtual TSerializationObject GetTestEntity(Ui ui)
		{
			return null;
		}

		/// <summary>
		/// Asserts the no unknown elements.
		/// </summary>
		/// <param name="serializationObjects">The serialization objects.</param>
		protected static void AssertNoUnknownElements(IEnumerable serializationObjects)
		{
			foreach (var serializationObject in serializationObjects.OfType<SerializationObject>())
				AssertNoUnknownElements(serializationObject);
		}

		/// <summary>
		/// Asserts the no unknown elements.
		/// </summary>
		/// <param name="serializationObject">The serialization object.</param>
		protected static void AssertNoUnknownElements(SerializationObject serializationObject)
		{
			Assert.IsNull(serializationObject.UnknownElements, "Serialization object '{0}' has unknown elements:\t{1}",
			              serializationObject,
			              serializationObject.UnknownElements != null
			              	? serializationObject.UnknownElements.Aggregate(String.Empty,
			              	                                                (seed, element) =>
			              	                                                seed + (seed.Length > 0 ? ", " : String.Empty) +
			              	                                                element.Name)
			              	: null);

			Assert.IsNull(serializationObject.UnknownAttributes, "Serialization object '{0}' has unknown attributes:\t{1}",
			              serializationObject,
			              serializationObject.UnknownAttributes != null
			              	? serializationObject.UnknownAttributes.Aggregate(String.Empty,
			              	                                                  (seed, attribute) =>
			              	                                                  seed + (seed.Length > 0 ? ", " : String.Empty) +
			              	                                                  attribute.Name)
			              	: null);
		}
	}
}